perm filename IOSAIL.BTH[UP,DOC]1 blob
sn#352208 filedate 1978-04-29 generic text, type C, neo UTF8
COMMENT ⊗ VALID 00018 PAGES
C REC PAGE DESCRIPTION
C00001 00001
C00003 00002 Changes to IOSAIL.
C00008 00003 Introduction.
C00010 00004 SPACE,TAB,BELL,FORMFD,LF,CR,CRLF,!,PROC,REF,THRU,UPTO,BEGINLOOP.
C00014 00005 NULLSTRING,CVBS,ALPHA,NUMER,MEMLOC,LOWBOUND,UPBOUND,NUMDIM.
C00017 00006 DEBUG,BUGON,BUGOFF.
C00023 00007 YES,NO,NUMERIC,LOWERCASE,UPPERCASE,ALPHABETIC,ALPHAMERIC.
C00025 00008 CAPITALIZE.
C00026 00009 ROUND.
C00027 00010 A discussion of channels.
C00030 00011 OPENREAD,OPENWRITE,CLOSEFILE,CLOSEALL.
C00033 00012 Filenames.
C00034 00013 The reads: LINE,INTEGER,REAL,STRING,WORD,YESNO,BUFFER,READ,RDTTY.
C00040 00014 EOF.
C00042 00015 GETPROMPT,SETPROMPT.
C00044 00016 SETREADER,GETREADER.
C00046 00017 SETECHO.
C00047 00018 TERM!PRINT!(ON | OFF),FILE!PRINT!(ON | OFF),FILE!PRINT!CLOSE.
C00049 ENDMK
C⊗;
Changes to IOSAIL.
∂4/28/78 -- LOADER BUG FIXED
The names FILE!PRINT!ON,FILE!PRINT!OFF,FILE!PRINT!CLOSE are too long
to be distinguished by the loader (since they were moved to IOSAIL.SAI)
therefore the procedures were renamed to be !!FPON,!!FPOFF,!!FPC and
the long names became macros in .HDR calling these new names for
the procedures. The change should be transparent to the user except
that the new bang-bang (!!) names of course become "reserved words".
∂4/27/78 -- FILE!PRINT!ON,FILE!PRINT!OFF,FILE!PRINT!CLOSE,CLOSEALL
have been moved from macros in .hdr to procedures in .sai to
permit PROFILE to make more reasonable output for these features.
In addition CLOSEALL now closes a SETPRINT file that may be open.
∂4/25/78 -- IOSAIL.DOC[107,bth] ==> IOSAIL.BTH[up,doc]
IOSAIL.HDR[107,bth] ==> IOSAIL.HDR[sub,sys]
IOSAIL.REL[107,bth] ==> IOSAIL.REL[sub,sys]
IOSAIL.SAI[107,bth] ==> IOSAIL.SAI[sub,sys]
∂2/23/78 -- openread and openwrite bug fixed
prompting for filenames in openread and openwrite will now
work correctly even with setprint in use (due to changing a
print to an outstr).
∂2/21/78 -- new macros
newr=new!record
nullr=null!record
∂2/21/78 -- ROUND
new ROUND function see iosail.doc
∂2/21/78 -- READINTEGER "fixed"
READINTEGER has been changed to use cvd rather that intscan since
it was found that intscan uses realscan and can lose some integers.
∂2/13/78 -- tab bug fixed
When the iosail package was brought from lots tabs were changed to spaces,
hence tabs were not passed over correctly in the input routine (eg. read
integer). This has been corrected.
∂2/7/78 -- new macros
R!C = record!class
R!P = record!pointer
∂1/30/78 -- openread / openwrite fixed
if on opening a filename, that file does not exist then iosail
now will permit the user to change device as well as file name.
∂1/26/78 -- availability of rdtty
RDTTY is now available for use by the user of IOSAIL.
∂1/26/78 -- rdtty correction
RDTTY now prompts with OUTSTR to the terminal which ignores
the status of setprint.
∂1/26/78 -- prompt changes
Instead of prompting with ?>integer> IOSAIL now prompts with ?>(integer)
and similarly for other types.
∂1/26/78 -- new READ
READ is no longer a synonym for READREAL. If the prompting is still the
default then READ will prompt with >? instead of ?>. This is for
compatibility with Floyd's notes.
∂1/26/78 -- READBUFFER
a new string procedure that returns anything left in the input buffer.
∂1/23/78 -- new macros
SPACE = " ".
NEWPAGE = FORMFEED.
∂1/23/78 -- rdtty
Rdtty has been incorporated into IOSAIL and setty has been eliminated.
Introduction.
This document describes the SAIL procedures and macros written by Kevin
Karplus <k.kjk> December 1977. The package is intended for the CS105 and
CS106 classes, but is available to all users of the SAIL language. The
package is being maintained at SU-AI by Brent Hailpern (BTH). All bugs
and suggetions should be sent to him.
To get all the macros and procedures declared, put the statement
REQUIRE "IOSAIL.HDR[SUB,SYS]" SOURCE!FILE;
in the declarations section of the outermost block of your SAIL program.
This will automatically include IOSAIL.REL[SUB,SYS] in the list of files
for the LOADER. The SAIL sources for the procedures are in
IOSAIL.SAI[SUB,SYS], the macros and declarations are in
IOSAIL.HDR[SUB,SYS].
There are several distinct parts to this package: macros whose function
is that of abbreviation, some macros for debugging, some simple procedures
for frequently used character tests, the input/output routines, and some
macros for use with SETPRINT.
SPACE,TAB,BELL,FORMFD,LF,CR,CRLF,!,PROC,REF,THRU,UPTO,BEGINLOOP.
(MARCOS defined in IOSAIL.HDR[SUB,SYS])
The following are simple abbreviations that have proven useful in
the past.
TAB ==> a string containing the tab character.
SPACE ==> a string containing the space character.
BELL ==> a string containing the bell character.
FORMFEED ==> a string containing the formfeed character ↑L.
NEWPAGE = FORMFD = FORMFEED
LF = LINEFEED ==> a string containing the linefeed character.
CR ==> a string containing a carriage return.
CRLF = NEWLINE ==> the equivalent of CR & LF.
PLOTLF ==> a string containing ↑E & LINEFEED. [LOTS]
! ==> COMMENT
PROC ==> PROCEDURE
REF ==> REFERENCE
THRU ==> step 1 until
UPTO ==> :=1 step 1 until
BEGINLOOP ==> while true do begin
R!C ==> record!class
R!P ==> record!pointer
NEWR ==> new!record
NULLR ==> null!record
Note that the BEGINLOOP macro along with the DONE construct can
be used as a looping construct of great power and simplicity. For
example:
beginloop "example loop"
temp:=READLINE;
if EOF then DONE "example loop";
! do here whatever you want with temp;
.
.
.
end "example loop";
this program fragment reads lines of the main input device until it
reaches an end of file. All the lines read are processed by the code
after the IF statement, and the the loop is terminated by satisfaction
of the end of file test. (The procedures READLINE and EOF are
described later.) The capitalization of the DONE statement is
encouraged, to make it stand out from the predominantly lower case
program body.
Please remember that the macro for comment (!) must have a
delimiter after it to be recognized as a character. Thus "!ABCD" is a
legal variable, but "! ABCD" is a comment.
The usual uses of FORMFEED, TAB, BELL, and CRLF are with the PRINT
statement to print them on your terminal (or with similar statements to
add them to files). You will find CRLF the most useful of this set of
characters. FORMFEED at the begining of a line tells the line printer to
go to a new page before printing the line. PLOTLF is not of much use for
novices. It is for printing a line in the "plotting" mode on the
PRINTRONIX at LOTS.
NULLSTRING,CVBS,ALPHA,NUMER,MEMLOC,LOWBOUND,UPBOUND,NUMDIM.
(MACROS defined in IOSAIL.HDR[SUB,SYS])
Some other macros of interest are
NULLSTRING(x) returns TRUE if the string x is the null string and
FALSE otherwise.
CVBS(x) converts boolean to string "TRUE" or "FALSE"
ALPHA ==> a string containing the entire alphabet in both
upper and lower cases.
NUMER ==> a string containing all the digits.
MEMLOC(x) ==> memory[location(x)]. (useful only to hackers)
LOWBOUND(arr,dim) returns the lower bound of the DIMth dimension of
the array ARR at runtime.
UPBOUND(arr,dim) returns the upper bound of the DIMth dimension of the
array ARR at runtime.
NUMDIM(arr) returns the number of dimensions of array ARR at
runtime.
ALPHA and NUMER have many uses, one of the most important being
to make it easier to set up "breaktables" for the SAIL SCAN statement.
Novices should not have to worry about this powerful but complicated
statement, as the routines described later should be sufficient for
their needs.
LOWBOUND, UPBOUND, and NUMDIM are useful if you use arrays with variable
dimensions, but need to know the dimensions at runtime to set loop
parameters. These macros are particularly useful inside of procedures,
which can then be handed arrays of any size and still be able to figure
out what to do with them. Note that NUMDIM will be negative for string
arrays (because of the expansion to the ARRINFO command).
DEBUG,BUGON,BUGOFF.
This is a very powerful macro originally designed by J.Q.Johnson,
later slightly modified by Kevin Karplus. It allows the user to print
out selected variables at any point in a program, along with
identification of the variables and the location in the program. The
output looks like:
DEBUG:after fiddling the foofram i=17.3; j=99.99; stq="jjjgh"jjitm";
which could have been created by a program segment like:
i:=17.3;
j:=99.99;
stq:="jjjgh""jjitm";
DEBUG(after fiddling the foofram,(i,j,stq));
Notice that the quote mark inside the string is not doubled by DEBUG,
and that the location identification is any string of characters that
satisfies the normal rules about arguments to macros. For simplicity,
it is best to keep this to a few short simple words, without commas or
other punctuation.
Like most macros, DEBUG gets confused by commas that it thinks
are superfluous or misplaced. Thus a statement like: DEBUG(before
input,(innersecrets[1,2,j])); is liable to give you an error message
(or a mysterious DRYROT). SAIL doesn't know about DEBUG, so the error
message will be exceedingly unhelpful. There is, of course, a way
around. (SAIL abounds in ways to get around things, it adds to its
mystery.) The particular hack involved is to put curly braces around
the variable that is causing the difficulty. Thus the statement
DEBUG(before input,({innersecrets[1,2,j]},j)); would work.
It is also possible to use expressions instead of variables as
the things to be printed. They will be printed as if they were in a
PRINT statement , and will be labeled with the expression that
produced them. Use of expressions is not guaranteed by the
implementors, so don't get upset if it doesn't work.
As a further convenience, if you have only a single variable to
print, you can omit the parentheses around it. "DEBUG(here,a);"
should be exactly the same as "DEBUG(here,(a));" .
Sometimes great quantities of debug output can be generated, even
when it is known that the bug you are looking for only occurs when
certain special conditions occur. While the computer cannot check the
phase of the moon or the absence of the TA, it is often easy to put in
checks to see whether the program has gotten a value that is strange
at some point, and then turn on the DEBUG statements. To make this
easier, two macros have been included: BUGON and BUGOFF. Normally
the DEBUG statements are "on" and print output on the terminal.
Execution of BUGOFF turns off the DEBUG statements until the next
execution of a BUGON. Note that the variable !bugoff is what is
changed by these macros, you shouldn't use that variable name for
anything else. As an example, assume that the routine BUGGY is known
to die when it has to handle long strings. Then something like this
fragment might help:
BUGOFF;
.
.
.
if length(arg) > 56 then BUGON else BUGOFF;
BUGGY(arg);
.
.
.
There is also a more general and more powerful debugging facility
available, called BAIL. For most short student programs, the effort
involved in learning and using BAIL is not justified by the small
extra debugging power it gives you.
YES,NO,NUMERIC,LOWERCASE,UPPERCASE,ALPHABETIC,ALPHAMERIC.
Some small simple procedures for dealing with strings
This section contains a number of very short, very simple procedures that
are frequently useful. All are boolean procedures (ie. they return
either TRUE or FALSE).
The argument to all the procedures is a character. This can be done
either by having the character be the first character of a string passed
to the procedure, or by passing an integer whose value is the ASCII
representation of the character. Passing a string for interpretation of
its first character is probably more common for most of these procedures.
YES(char) returns TRUE if the character is "Y" or "y".
NO(char) returns TRUE if the character is "N" or "n".
NUMERIC(char) returns TRUE if the character is a decimal digit.
LOWERCASE(char) returns TRUE if the character is a lower case letter.
UPPERCASE(char) returns TRUE if the character is an upper case letter.
ALPHABETIC(char) returns TRUE if the character is a letter.
ALPHAMERIC(char) returns TRUE if the character is either a letter or a
digit.
CAPITALIZE.
CAPITALIZE(string) returns its string argument with all the lower case
letters converted to upper case (or, to be old fashioned,
all the miniscules converted to magiscules). Thus
CAPITALIZE("abcDEFmmm123 $ $") returns "ABCDEFMMM123 $ $".
Note that the argument is left unchanged.
ROUND.
The round procedure does floor(x+.5) despite the compile switch setting
for real to integer coersion (it uses the FIXR FAIL command).
To be used:
i:=round(x)
where i is integer and x is real.
A discussion of channels.
Whenever input and output are mentioned in respect to SAIL, channels
are almost certain to be mentioned. A channel (in this context) is
simply a small number that gets associated with the file or device you
are communicating with, so it isn't necessary to refer to the whole
name of the file and look for it each time you want to access it. The
numbers are rather arbitrarily assigned, and have no intrinsic
meaning.
SAIL can handle about 16 channels (0 through 15) plus the controlling
terminal (also known as TTY:) as channel -1. Channel -2 is also the
terminal. The channels used by this package are the same as SAIL channels
except:
1. When inputing through the procedures of this package, channel
-1 means the "main reader channel". This is the terminal,
unless SETREADER is called to change it.
2. When inputting though the procedures of this package, channel
-2 means the controlling terminal.
3. All the SAIL functions behave as normal, do NOT try printing on
-1 nor should you try printing on channel -2. To output to the
terminal use PRINT.
4. The normal mechanisms for opening and closing files should
not be employed if the files are to be used with the other
routines of this package.
OPENREAD,OPENWRITE,CLOSEFILE,CLOSEALL.
The process of finding the file you want to use and associating a
channel number with it is called "opening a file". The similar
process of cleaning out the buffers and putting the file away when you
are done is called "closing a file".
There are two routines in IOSAIL for opening a file, called OPENREAD
and OPENWRITE. Both take the file name as an argument and both return
the channel associated with the file. If no filename is given, (or if
the null string is given) then the OPEN... procedures ask for the
file name from the teletype. Note that TTY: is an acceptable
filename for both input and output (it means the terminal you are
running the program from).
The format of the commands looks like:
chan:=OPENREAD;
chan:=OPENREAD("FOO.in");
chan:=OPENWRITE;
chan:=OPENWRITE("FOO.OUT");
Please resist the temptation to use the .SAI extension for your output
files. It is very easy to destroy programs that way.
The counterpart of "opening", "closing", is handled just as easily.
CLOSEFILE(chan) will close the file and free the channel number for
reassignment. CLOSEFILE(-1) will close the reader channel and return
channel -1 to its original status as the controlling input terminal.
There is a procedure CLOSEALL, which will close all the channels that are
open (including any setprint file), and generally try to set the input
output system as much like its initial state as it can. To do this
massive clean-up, make:
CLOSEALL;
the last executable statement of your program.
Filenames.
It is possible to determine the filename of a channel that has been
opened by use of the string procedure FILENAME(chan). If the channel
is connected to a terminal the string "TTY:" is returned.
The reads: LINE,INTEGER,REAL,STRING,WORD,YESNO,BUFFER,READ,RDTTY.
Procedures for input
There are several procedures for inputting various values from a channel.
All of them have the same basic calling sequence, and behave in similar
manners (except for RDTTY which is described below). They are: READLINE,
READINTEGER, READREAL, READSTRING, READWORD, READYESNO, READ, READBUFFER.
The calling sequence is
foo:=READ...(chan);
foo:=READ...;
where READ... means any of the READ routines mentioned above, and foo
is of the appropriate type for the particular routine. If chan is
given, then the channel must be open for reading. If chan is not
given, -1 is assumed and reading is from the reader channel.
READLINE is the most basic of these routines, and is called by all the
others. It reads a line (ignoring carriage return) until it finds a
linefeed, or end-of-file is reached. If there are no more lines to be
read from the file, a null string is returned, otherwise the string
read (without crlf) is returned.
READINTEGER and READREAL ignore blank, tabs, and crlfs until they get
to a non-blank character. If the character is not acceptable for the
first character of a number, an error message is generated. If the
channel is connected to a terminal, then the user is prompted for a
retry. Otherwise the error is fatal, and the program aborts.
READ is the same as READREAL except that if the prompting on the channel
is the default then the prompting for that particular read becomes >?
instead of ?>.
READSTRING reads a string beginning and ending with the quote
character ("). A quote can be included in the string by doubling it.
Thus the string "abc""DEF""ghi" would be read as abc"DEF"ghi .
Blanks, tabs, and crlfs before the opening " are ignored. If the
first character after the blanks is not a quote, an error message is
printed. (Again, fatal if not TTY, retry if TTY).
READWORD reads the next punctuation mark or string of alphameric
characters. Leading blanks, tabs, and crlfs are ignored.
READYESNO expects the next character (after blanks, tabs, and crlfs)
to be "Y","y", "N", or "n". It then returns TRUE if it is a y and
FALSE if it is an n. Any other character results in an error. (Fatal
if not a TTY, retry if a tty).
READBUFFER returns the unread portion of the input buffer as a string.
RDTTY is called by RDTTY("prompt"), where ">" is default. It prints that
prompt on the terminal (ignoring SETPRINTs) and then reads a line from the
terminal and returns it as a string. RDTTY is faster in that it avoids
all overhead of READLINE(-2) and permits direct specification of the
prompt. Its drawback is that it bypasses the eof mechanism for the
terminal and should therefore be used with care.
Note: READLINE always reads a fresh line from the terminal or file,
all the other routines will keep reading on the same line until there
is nothing left of the line, or a READLINE is done. Thus it is
possible to read every non-blank of a file with repeated READWORD, or
every number with READREAL. Line numbers in files are ignored.
EOF.
End-of-file handling
When one of the READ... routines is called, and can't find anymore
input, it returns an appropriate null value. (Null strings for string
valued procedures, FALSE for READYESNO, and intscan(null) or
realscan(null) for READINTEGER and READREAL). The EOF indication can
be tested with the procedure EOF(chan). As with all the procedures,
if the channel number is omitted, channel -1 s assumed. EOF will
return TRUE, after the call which returned the null value.
Thus the correct test for end-of-file is:
BEGINLOOP "readloop"
temp:=READ...;
if EOF then DONE "readloop";
! body of loop;
.
.
.
END "readloop"
Testing for end-of-file should be done after every read. An end-of-file
for display can be accomplished by typing <control><meta><lf> (or <ctrl>z
from a TTY). However, only one null return is made, subsequent calls to
read from a channel associated with a terminal will behave normally, and
EOF will only return TRUE if the last read on the channel hit the EOF
character.
GETPROMPT,SETPROMPT.
Prompting
When reading from the terminal, the user is prompted for input. The
default prompt for channels -2 and -1 is "?>", for the other channels
it is the channel number followed by ">". The prompt string can be
determined by foo:=GETPROMPT(chan), and changed by
SETPROMPT(string,chan).
If the channel is not specified, then channel -1 is assumed. If the
string to SETPROMPT is null then the prompt will be set back to the
default.
If the promptstring for a channel is the default string, then the
routines READINTEGER, READREAL, READSTRING, READWORD, READYESNO add a
further prompt to indicate what type of input is being requested.
SETREADER,GETREADER.
The use of channel -1 to access the main reader channel has been
mentioned before. To associate -1 with some real channel is very
easy. First, OPENREAD should be called to create the channel, then
SETREADER(chan) will make chan the main reader channel. The reader
channel can be reset to the terminal (the initial state) by doing
SETREADER(-1). SETREADER without an argument is equivalent to
SETREADER(-1).
GETREADER is an integer valued procedure with no arguments which
returns the channel number of the main reader channel. If no
SETREADER call has been made, then GETREADER returns -1.
SETECHO.
Echoing
All the input through the main reader channel can be echoed to a file,
or to the terminal. This is accomplished by doing an OPENWRITE to
open a channel, and SETECHO(chan) to send the echoing to the channel.
SETECHO(-1) or SETECHO without arguments, turns off the echoing. The
echo can be forced to the terminal by SETECHO(-2).
Note that you cannot echo to the file used in a SETPRINT statement,
because of the way SAIL handles SETPRINT. However, it is possible to
do CPRINT to the same channel as the echoing.
TERM!PRINT!(ON | OFF),FILE!PRINT!(ON | OFF),FILE!PRINT!CLOSE.
Setprint control macors and procedures
There are two macros and three procedures in this class, and they all
invoke SETPRINT with various arguments. They serve to direct your PRINT
output to your terminal, a file, or both. Normally your PRINT output goes
just to your terminal, so you have to do something special if you want the
output to go to a file as well. You could open a channel and write to it
(using OPENWRITE and CPRINT), but this necessitates duplicating the PRINT
statements and is often not worth the trouble.
First the file should be established with the SETPRINT(filename,"B");
statement as described int the SAIL manual. Thereafter TERM!PRINT!ON,
TERM!PRINT!OFF, FILE!PRINT!ON, FILE!PRINT!OFF, FILE!PRINT!CLOSE should
suffice for handling the various SETPRINT options.
Note: the FILE!PRINT!... identifiers are actually macros calling the
three procedures !!FPON,!!FPOFF,!!FPC so avoid using these bang-bang (!!)
identifiers as variables in your programs.